﻿#pragma once

namespace fast_io
{

namespace details
{

template <bool cr = false, ::std::integral char_type>
inline constexpr deco_result<char_type, char_type>
simd_lf_crlf_process_chars(char_type const *fromfirst, char_type const *fromlast, char_type *tofirst,
						   char_type *tolast) noexcept
{
	constexpr ::std::size_t initialdiffn{::fast_io::details::optimal_simd_vector_run_with_cpu_instruction_size};
	constexpr unsigned N{initialdiffn / sizeof(char_type)};
	using simd_vector_type = ::fast_io::intrinsics::simd_vector<char_type, N>;
	constexpr char_type lfchct{char_literal_v<u8'\n', ::std::remove_cvref_t<char_type>>};
	constexpr char_type lfchr{char_literal_v<u8'\r', ::std::remove_cvref_t<char_type>>};

	constexpr char_type tofdch{cr ? lfchr : lfchct};
#if (__cpp_lib_bit_cast >= 201806L) && !defined(__clang__)
	constexpr simd_vector_type charsvec{::std::bit_cast<simd_vector_type>(characters_array_impl<tofdch, char_type, N>)};
#else
	simd_vector_type charsvec;
	charsvec.load(characters_array_impl<tofdch, char_type, N>.data());
#endif
	simd_vector_type vec;
	for (; fromfirst != fromlast && tofirst != tolast; ++fromfirst)
	{
		if (*fromfirst != tofdch)
		{
			*tofirst = *fromfirst;
			++fromfirst;
			++tofirst;
			::std::size_t fromdiff{static_cast<::std::size_t>(fromlast - fromfirst)};
			::std::size_t todiff{static_cast<::std::size_t>(tolast - tofirst)};
			if (todiff < fromdiff)
			{
				fromdiff = todiff;
			}
			fromdiff /= N;
			for (; fromdiff; --fromdiff)
			{
				vec.load(fromfirst);
				auto comres{vec != charsvec};
				if (!::fast_io::intrinsics::is_all_zeros(comres))
				{
					break;
				}
				vec.store(tofirst);
				fromfirst += N;
				tofirst += N;
			}
			if (!fromdiff)
			{
				break;
			}
			::std::size_t fromdiff2{static_cast<::std::size_t>(fromlast - fromfirst)};
			::std::size_t todiff2{static_cast<::std::size_t>(tolast - tofirst)};
			if (todiff2 < fromdiff2)
			{
				fromdiff2 = todiff2;
			}
			for (; fromdiff2; --fromdiff2)
			{
				auto ch{*fromfirst};
				if (ch == tofdch)
				{
					break;
				}
				*tofirst = ch;
				++fromfirst;
				++tofirst;
			}
			if (!fromdiff2)
			{
				break;
			}
		}
		if constexpr (cr)
		{
			*tofirst = lfchct;
			++tofirst;
			*tofirst = lfchr;
			++tofirst;
		}
		else
		{
			*tofirst = lfchr;
			++tofirst;
			*tofirst = lfchct;
			++tofirst;
		}
	}
	return {fromfirst, tofirst};
}

template <bool cr = false, ::std::integral char_type>
inline constexpr deco_result<char_type, char_type>
simd_crlf_lf_process_chars(char_type const *fromfirst, char_type const *fromlast, char_type *tofirst,
						   char_type *tolast) noexcept
{
	constexpr ::std::size_t initialdiffn{::fast_io::details::optimal_simd_vector_run_with_cpu_instruction_size};
	constexpr unsigned N{initialdiffn / sizeof(char_type)};
	constexpr unsigned Np1{N + 1u};
	using simd_vector_type = ::fast_io::intrinsics::simd_vector<char_type, N>;
	constexpr char_type lfchct{char_literal_v<u8'\n', ::std::remove_cvref_t<char_type>>};
	constexpr char_type lfchr{char_literal_v<u8'\r', ::std::remove_cvref_t<char_type>>};
#if (__cpp_lib_bit_cast >= 201806L) && !defined(__clang__)
	constexpr simd_vector_type charsvec{::std::bit_cast<simd_vector_type>(characters_array_impl<lfchr, char_type, N>)};
#else
	simd_vector_type charsvec;
	charsvec.load(characters_array_impl<lfchr, char_type, N>.data());
#endif
	for (simd_vector_type vec;;)
	{
		::std::size_t fromdiff{static_cast<::std::size_t>(fromlast - fromfirst)};
		::std::size_t todiff{static_cast<::std::size_t>(tolast - tofirst)};
		if (fromdiff < Np1 || todiff < N)
		{
			break;
		}
		vec.load(fromfirst);
		auto comres{vec != charsvec};
		vec.store(tofirst);
		if (::fast_io::intrinsics::is_all_zeros(comres))
		{
			fromfirst += N;
			tofirst += N;
			continue;
		}
		unsigned pos{::fast_io::intrinsics::vector_mask_countr_one(comres)};
		fromfirst += pos + 1;
		tofirst += pos + 1;
		if (*fromfirst != lfchct)
		{
			continue;
		}
		++fromfirst;
		if constexpr (cr)
		{
			tofirst[-1] = lfchr;
		}
		else
		{
			tofirst[-1] = lfchct;
		}
	}
	return {fromfirst, tofirst};
}

template <bool cr, ::std::integral char_type>
inline constexpr deco_result<char_type, char_type>
simd_lf_cr_process_chars(char_type const *fromfirst, char_type const *fromlast, char_type *tofirst,
						 char_type *tolast) noexcept
{
	constexpr ::std::size_t initialdiffn{::fast_io::details::optimal_simd_vector_run_with_cpu_instruction_size};
	constexpr unsigned N{initialdiffn / sizeof(char_type)};
	using simd_vector_type = ::fast_io::intrinsics::simd_vector<char_type, N>;
	constexpr char_type lfchct{char_literal_v<u8'\n', ::std::remove_cvref_t<char_type>>};
	constexpr char_type lfchr{char_literal_v<u8'\r', ::std::remove_cvref_t<char_type>>};
	constexpr char_type tofdch{cr ? lfchr : lfchct};
#if (__cpp_lib_bit_cast >= 201806L) && !defined(__clang__)
	constexpr simd_vector_type charsvec{::std::bit_cast<simd_vector_type>(characters_array_impl<tofdch, char_type, N>)};
	constexpr simd_vector_type threevec{::std::bit_cast<simd_vector_type>(characters_array_impl<3, char_type, N>)};
#else
	simd_vector_type charsvec;
	charsvec.load(characters_array_impl<tofdch, char_type, N>.data());
	simd_vector_type threevec;
	threevec.load(characters_array_impl<3, char_type, N>.data());
#endif

	for (simd_vector_type vec;;)
	{
		::std::size_t fromdiff{static_cast<::std::size_t>(fromlast - fromfirst)};
		::std::size_t todiff{static_cast<::std::size_t>(tolast - tofirst)};
		if (fromdiff < N || todiff < N)
		{
			break;
		}
		vec.load(fromfirst);
		auto comres{vec == charsvec};
		auto comresandres{comres & threevec};
		if constexpr (cr)
		{
			vec -= comresandres;
		}
		else
		{
			vec += comresandres;
		}
		vec.store(tofirst);
		fromfirst += N;
		tofirst += N;
	}
	return {fromfirst, tofirst};
}

} // namespace details

} // namespace fast_io
